// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package log

import (
	"context"
	"gitee.com/go-wares/log/base"
	"gitee.com/go-wares/log/conf"
	"gitee.com/go-wares/log/plugins/adapters/elastic"
	"gitee.com/go-wares/log/plugins/adapters/file"
	"gitee.com/go-wares/log/plugins/adapters/kafka"
	"gitee.com/go-wares/log/plugins/adapters/term"
	"gitee.com/go-wares/log/plugins/reporters/jaeger"
	"gitee.com/go-wares/log/plugins/reporters/zipkin"
	"sync"
)

var (
	// Manager
	// is a component for managing logging and trace.
	Manager *Management

	once = new(sync.Once)
)

// Management
// is a component for managing logging and trace.
type Management struct {
	config        *conf.Config
	emptyLogger   *emptyLogger
	emptyReporter *emptyReporter
	logger        base.Logger
	reporter      base.Reporter
}

// Config
// returns the configuration of the log management.
func (o *Management) Config() *conf.Config {
	return o.config
}

// ConfigLoggerAdapter
// configure the logger adapter.
func (o *Management) ConfigLoggerAdapter(adapter base.Adapter) {
	// Stop
	// bucket of previous adapter.
	if o.logger != nil {
		if v := o.logger.GetBucket(); v != nil {
			v.Stop()
		}
	}

	// Switch
	// with configuration.
	switch adapter {
	case base.AdapterElasticsearch:
		o.logger = elastic.NewAdapter()
	case base.AdapterFile:
		o.logger = file.NewAdapter()
	case base.AdapterKafka:
		o.logger = kafka.NewAdapter()
	case base.AdapterTerm:
		o.logger = term.NewAdapter()
	default:
		o.logger = o.emptyLogger
	}
}

// ConfigTracerReporter
// configure the tracer reporter.
func (o *Management) ConfigTracerReporter(tracer base.Tracer) {
	// Stop
	// bucket of previous trace reporter.
	if o.reporter != nil {
		if v := o.reporter.GetBucket(); v != nil {
			v.Stop()
		}
	}

	// Switch
	// with configuration.
	switch tracer {
	case base.TracerJaeger:
		o.reporter = jaeger.NewReporter()
	case base.TracerZipkin:
		o.reporter = zipkin.NewReporter()
	default:
		o.reporter = o.emptyReporter
	}
}

// GetLoggerAdapter
// returns the latest logger adapter.
func (o *Management) GetLoggerAdapter() base.Logger {
	return o.logger
}

// GetTracerReporter
// returns the latest tracer reporter.
func (o *Management) GetTracerReporter() base.Reporter {
	return o.reporter
}

// Log
// report the log with level and contents.
func (o *Management) Log(ctx context.Context, field map[string]any, level base.Level, format string, args ...any) {
	o.LogItem(base.NewItem(ctx, field, level, format, args...))
}

// LogItem
// report the log with item.
func (o *Management) LogItem(item *base.Item) {
	o.logger.Log(item)
}

// Report
// reports the span of a trace.
func (o *Management) Report(span base.Span) {
	o.reporter.Publish(span)
}

// StopAndWait
// stops the log management and waits for the log to be reported.
func (o *Management) StopAndWait() {
	if v := o.reporter.GetBucket(); v != nil {
		v.Wait()
	}
	if v := o.logger.GetBucket(); v != nil {
		v.Wait()
	}
}

// +---------------------------------------------------------------------------+
// | Access methods                                                            |
// +---------------------------------------------------------------------------+

func (o *Management) init() *Management {
	o.config = conf.New(o)
	o.emptyLogger = &emptyLogger{}
	o.emptyReporter = &emptyReporter{}
	return o
}

func init() {
	once.Do(func() {
		Manager = (&Management{}).init()
	})
}
